use std::fmt;
use std::fmt::{Debug, Display, Formatter};
use std::ops::{Add, Sub};

use crate::gen_title;

pub fn ch2_18_1() {
    gen_title("ch2_18_1", 18);

    pub trait Summary {
        fn summarize(&self) -> String;
    }

    pub struct Post {
        pub title: String,
        pub author: String,
        pub content: String,
    }

    impl Summary for Post {
        fn summarize(&self) -> String {
            format!("文章{}, 作者是: {}", self.title, self.author)
        }
    }

    pub struct Weibo {
        pub username: String,
        pub content: String,
    }

    impl Summary for Weibo {
        fn summarize(&self) -> String {
            format!("{}发表了微博{}", self.username, self.content)
        }
    }

    let post = Post {
        title: "rust learning".to_string(),
        author: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("post summary: {}", post.summarize());

    let weibo = Weibo {
        username: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("weibo summary: {}", weibo.summarize());
}

pub fn ch2_18_2() {
    gen_title("ch2_18_2", 18);

    pub trait Summary {
        fn summarize(&self) -> String {
            String::from("(Read more...)")
        }
    }

    pub struct Post {
        pub title: String,
        pub author: String,
        pub content: String,
    }

    impl Summary for Post {}

    pub struct Weibo {
        pub username: String,
        pub content: String,
    }

    impl Summary for Weibo {
        fn summarize(&self) -> String {
            format!("{}发表了微博{}", self.username, self.content)
        }
    }

    let post = Post {
        title: "rust learning".to_string(),
        author: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("post summary: {}", post.summarize());

    let weibo = Weibo {
        username: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("weibo summary: {}", weibo.summarize());
}

pub fn ch2_18_3() {
    gen_title("ch2_18_3", 18);

    pub trait Summary {
        fn summarize_author(&self) -> String;
        fn summarize(&self) -> String {
            format!("(Read more from {}...)", self.summarize_author())
        }
    }

    pub struct Post {
        pub title: String,
        pub author: String,
        pub content: String,
    }

    impl Summary for Post {
        fn summarize_author(&self) -> String {
            self.author.to_string()
        }
    }

    pub struct Weibo {
        pub username: String,
        pub content: String,
    }

    impl Summary for Weibo {
        fn summarize_author(&self) -> String {
            String::from("misiro")
        }

        fn summarize(&self) -> String {
            format!("{}发表了微博{}", self.username, self.content)
        }
    }

    let post = Post {
        title: "rust learning".to_string(),
        author: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("post summary: {}", post.summarize());

    let weibo = Weibo {
        username: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("weibo summary: {}", weibo.summarize());
}

pub fn ch2_18_4() {
    gen_title("ch2_18_4", 18);

    pub trait Summary {
        fn summarize_author(&self) -> String;
        fn summarize(&self) -> String {
            format!("(Read more from {}...)", self.summarize_author())
        }
    }

    #[derive(Debug)]
    pub struct Post {
        pub title: String,
        pub author: String,
        pub content: String,
    }

    impl Summary for Post {
        fn summarize_author(&self) -> String {
            self.author.to_string()
        }
    }

    #[derive(Debug)]
    pub struct Weibo {
        pub username: String,
        pub content: String,
    }

    impl Summary for Weibo {
        fn summarize_author(&self) -> String {
            String::from("misiro")
        }

        fn summarize(&self) -> String {
            format!("{}发表了微博{}", self.username, self.content)
        }
    }

    pub fn notify(item: &(impl Summary + Debug)) {
        println!("item: {:?}", item);
    }

    let post = Post {
        title: "rust learning".to_string(),
        author: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("post summary: {}", post.summarize());
    notify(&post);

    let weibo = Weibo {
        username: "misiro".to_string(),
        content: "learning rust is useful".to_string(),
    };
    println!("weibo summary: {}", weibo.summarize());
    notify(&weibo);
}

pub fn ch2_18_5() {
    gen_title("ch2_18_5", 18);

    fn some_function1<T: Display + Clone + std::ops::Add<U, Output = i32>, U: Clone + Debug>(
        t: T,
        u: U,
    ) -> i32 {
        println!("t: {}", t);
        println!("u: {:?}", u);
        t + u
    }

    fn some_function2<T, U>(t: T, u: U) -> i32
    where
        T: Display + Clone + std::ops::Add<U, Output = i32>,
        U: Clone + Debug,
    {
        println!("t: {}", t);
        println!("u: {:?}", u);
        t + u
    }

    some_function1(1, 2);
    some_function2(1, 2);
    println!("f1: {}, f2: {}", some_function1(1, 2), some_function2(1, 2));
}

pub fn ch2_18_6() {
    gen_title("ch2_18_6", 18);

    struct Pair<T> {
        x: T,
        y: T,
    }

    impl<T> Pair<T> {
        fn new(x: T, y: T) -> Self {
            Self { x, y }
        }
    }

    impl<T: Display + PartialOrd> Pair<T> {
        fn cmp_display(&self) {
            if self.x >= self.y {
                println!("The largest member is x = {}", self.x);
            } else {
                println!("The largest member is y = {}", self.y);
            }
        }
    }

    let pair = Pair::new(1, 2);

    pair.cmp_display();
}

pub fn ch2_18_7() {
    gen_title("ch2_18_7", 18);

    #[derive(Debug)]
    struct Pair<T> {
        x: T,
        y: T,
    }

    fn returns_pair() -> impl Debug {
        Pair::<usize> { x: 18, y: 18 }
    }

    println!("pair: {:?}", returns_pair());
}

pub fn ch2_18_8() {
    gen_title("ch2_18_8", 18);

    fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
        let mut largest = list[0];

        for &item in list.iter() {
            if item > largest {
                largest = item;
            }
        }

        largest
    }

    let number_list = vec![34, 50, 25, 100, 65];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'a', 'a', 'q'];
    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

pub fn ch2_18_9() {
    gen_title("ch2_18_9", 18);

    let a: i32 = 10;
    let b: i16 = 100;

    let b_ = b.try_into().unwrap();

    if a < b_ {
        println!("Ten is less than one hundred.");
    }
}

pub fn ch2_18_10() {
    gen_title("ch2_18_10", 18);

    #[derive(Debug)]
    struct Point<T: Add<T, Output = T>> {
        x: T,
        y: T,
    }

    impl<T: Add<T, Output = T>> Add for Point<T> {
        type Output = Point<T>;

        fn add(self, p: Point<T>) -> Point<T> {
            Point {
                x: self.x + p.x,
                y: self.y + p.y,
            }
        }
    }

    fn add<T: Add<T, Output = T>>(a: T, b: T) -> T {
        a + b
    }

    let p1 = Point {
        x: 1.1f32,
        y: 1.1f32,
    };
    let p2 = Point {
        x: 2.1f32,
        y: 2.1f32,
    };
    println!("{:?}", add(p1, p2));

    let p3 = Point { x: 1i32, y: 1i32 };
    let p4 = Point { x: 1i32, y: 1i32 };
    println!("{:?}", p3 + p4);
}

pub fn ch2_18_11() {
    gen_title("ch2_18_11", 18);

    #[derive(Debug, PartialEq)]
    enum FileState {
        Open,
        Closed,
    }

    #[derive(Debug)]
    struct File {
        name: String,
        data: Vec<u8>,
        state: FileState,
    }

    impl Display for FileState {
        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
            match self {
                FileState::Open => write!(f, "OPEN"),
                FileState::Closed => write!(f, "CLOSED"),
            }
        }
    }

    impl Display for File {
        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
            write!(f, "<{} ({})>", self.name, self.state)
        }
    }

    impl File {
        fn new(name: &str) -> File {
            File {
                name: String::from(name),
                data: Vec::new(),
                state: FileState::Closed,
            }
        }
    }

    let file = File::new("f6.txt");
    println!("{:?}", file);
    println!("{}", file);
}

pub fn ch2_18_pra_1() {
    gen_title("ch2_18_pra_1", 18);

    struct Sheep {
        naked: bool,
        name: String,
    }

    impl Sheep {
        fn is_naked(&self) -> bool {
            self.naked
        }

        fn shear(&mut self) {
            if self.is_naked() {
                println!("{} is already naked...", self.name);
            } else {
                println!("{} gets a haircut!", self.name);
                self.naked = true;
            }
        }
    }

    trait Animal {
        fn new(name: String) -> Self;

        fn name(&self) -> String;

        fn noise(&self) -> String;

        fn talk(&self) {
            println!("{} says {}", self.name(), self.noise());
        }
    }

    impl Animal for Sheep {
        fn new(name: String) -> Self {
            Sheep { name, naked: false }
        }

        fn name(&self) -> String {
            self.name.clone()
        }

        fn noise(&self) -> String {
            if self.is_naked() {
                "baaaaaaah?".to_string()
            } else {
                "baaaaaah!".to_string()
            }
        }

        fn talk(&self) {
            println!("{} pauses briefly... {}", self.name, self.noise());
        }
    }
    let mut dolly: Sheep = Animal::new("Dolly".to_string());

    dolly.talk();
    dolly.shear();
    dolly.talk();
}

pub fn ch2_18_pra_2() {
    gen_title("ch2_18_pra_2", 18);

    trait Hello {
        fn say_hi(&self) -> String {
            String::from("hi")
        }

        fn say_something(&self) -> String;
    }

    struct Student {}

    impl Hello for Student {
        fn say_something(&self) -> String {
            String::from("I'm a good student")
        }
    }

    struct Teacher {}

    impl Hello for Teacher {
        fn say_something(&self) -> String {
            String::from("I'm not a bad teacher")
        }
    }

    let s = Student {};

    println!("{}", s.say_hi());
    println!("{}", s.say_something());

    let t = Teacher {};

    println!("{}", t.say_hi());
    println!("{}", t.say_something());
}

pub fn ch2_18_pra_3() {
    gen_title("ch2_18_pra_3", 18);

    #[derive(PartialEq, PartialOrd)]
    struct Centimeters(f64);

    #[derive(Debug)]
    struct Inches(i32);

    impl Inches {
        fn to_centimeters(&self) -> Centimeters {
            let &Inches(inches) = self;
            Centimeters(inches as f64 * 2.54)
        }
    }

    #[derive(Debug, PartialEq, PartialOrd)]
    struct Seconds(i32);

    let _one_second = Seconds(1);

    println!("One second looks like {:?}", _one_second);
    let _this_is_true = _one_second == _one_second;
    let _this_is_false = _one_second > _one_second;
    println!(
        "One second is {} one second.",
        if _this_is_true {
            "equal to"
        } else {
            "not equal to"
        }
    );

    let foot = Inches(12);

    println!("One foot equals {:?}", foot);

    let meter = Centimeters(100.0);

    let cmp = if foot.to_centimeters() < meter {
        "smaller"
    } else {
        "bigger"
    };
    println!("One foot is {} than one meter.", cmp);
}

pub fn ch2_18_pra_4() {
    gen_title("ch2_18_pra_4", 18);

    fn multiply<T: std::ops::Mul<T, Output = T>>(a: T, b: T) -> T {
        a * b
    }

    assert_eq!(6, multiply(2u8, 3u8));
    assert_eq!(5.0, multiply(1.0, 5.0));

    println!("{}", multiply(1.0, 5.0));
}

pub fn ch2_18_pra_5() {
    gen_title("ch2_18_pra_5", 18);

    #[derive(Debug)]
    struct Foo;

    #[derive(Debug)]
    struct Bar;

    #[derive(Debug, PartialEq)]
    struct FooBar;

    #[derive(Debug, PartialEq)]
    struct BarFoo;

    impl Add<Bar> for Foo {
        type Output = FooBar;

        fn add(self, _rhs: Bar) -> FooBar {
            FooBar
        }
    }

    impl Sub<Foo> for Bar {
        type Output = BarFoo;

        fn sub(self, _rhs: Foo) -> BarFoo {
            BarFoo
        }
    }

    assert_eq!(Foo + Bar, FooBar);
    assert_eq!(Bar - Foo, BarFoo);

    println!("{:?}", Foo + Bar);
    println!("{:?}", Bar - Foo);
}

pub fn ch2_18_pra_6() {
    gen_title("ch2_18_pra_6", 18);

    trait Summary {
        fn summarize(&self) -> String;
    }

    #[derive(Debug)]
    struct Post {
        title: String,
        author: String,
        content: String,
    }

    impl Summary for Post {
        fn summarize(&self) -> String {
            format!("{} by {} ({})", self.title, self.author, self.content)
        }
    }

    #[derive(Debug)]
    struct Weibo {
        username: String,
        content: String,
    }

    impl Summary for Weibo {
        fn summarize(&self) -> String {
            format!("{} published a weibo {}", self.username, self.content)
        }
    }

    let post = Post {
        title: "Popular Rust".to_string(),
        author: "Sunface".to_string(),
        content: "Rust is awesome!".to_string(),
    };

    let weibo = Weibo {
        username: "sunface".to_string(),
        content: "Weibo seems to be worse than Tweet".to_string(),
    };

    summary(&post);
    summary(&weibo);

    println!("{:?}", post);
    println!("{:?}", weibo);

    fn summary(item: &impl Summary) {
        println!("{}", item.summarize());
    }
}

pub fn ch2_18_pra_7() {
    gen_title("ch2_18_pra_7", 18);

    struct Sheep {}

    struct Cow {}

    trait Animal {
        fn noise(&self) -> String;
    }

    impl Animal for Sheep {
        fn noise(&self) -> String {
            "baaaaah!".to_string()
        }
    }

    impl Animal for Cow {
        fn noise(&self) -> String {
            "mooooooo!".to_string()
        }
    }

    fn random_animal(random_number: f64) -> Box<dyn Animal> {
        if random_number < 0.5 {
            Box::new(Sheep {})
        } else {
            Box::new(Cow {})
        }
    }

    let random_number = 0.234;
    let animal = random_animal(random_number);
    println!(
        "You've randomly chosen an animal, and it says {}",
        animal.noise()
    );
}

pub fn ch2_18_pra_8() {
    gen_title("ch2_18_pra_8", 18);

    fn sum<T: Add<T, Output = T>>(x: T, y: T) -> T {
        x + y
    }

    assert_eq!(sum(1, 2), 3);
    println!("{}", sum(1, 2));
}

pub fn ch2_18_pra_9() {
    gen_title("ch2_18_pra_9", 18);

    struct Pair<T> {
        x: T,
        y: T,
    }

    impl<T> Pair<T> {
        fn new(x: T, y: T) -> Self {
            Self { x, y }
        }
    }

    impl<T: Debug + PartialOrd> Pair<T> {
        fn cmp_display(&self) {
            if self.x >= self.y {
                println!("The largest member is x = {:?}", self.x);
            } else {
                println!("The largest member is y = {:?}", self.y);
            }
        }
    }

    #[derive(Debug, PartialOrd, PartialEq)]
    struct Unit(i32);

    let pair = Pair {
        x: Unit(1),
        y: Unit(2),
    };

    pair.cmp_display();
}

pub fn ch2_18_pra_10() {
    gen_title("ch2_18_pra_11", 18);

    fn example1() {
        struct Cacher<T: Fn(u32) -> u32> {
            calculation: T,
            value: Option<u32>,
        }

        impl<T: Fn(u32) -> u32> Cacher<T> {
            fn new(calculation: T) -> Cacher<T> {
                Cacher {
                    calculation,
                    value: None,
                }
            }

            fn value(&mut self, arg: u32) -> u32 {
                match self.value {
                    Some(v) => v,
                    None => {
                        let v = (self.calculation)(arg);
                        self.value = Some(v);
                        v
                    }
                }
            }
        }

        let mut cacher = Cacher::new(|x| x + 1);
        assert_eq!(cacher.value(10), 11);
        assert_eq!(cacher.value(15), 11);

        println!("{}", cacher.value(10));
    }

    fn example2() {
        struct Cacher<T>
        where
            T: Fn(u32) -> u32,
        {
            calculation: T,
            value: Option<u32>,
        }

        impl<T> Cacher<T>
        where
            T: Fn(u32) -> u32,
        {
            fn new(calculation: T) -> Cacher<T> {
                Cacher {
                    calculation,
                    value: None,
                }
            }

            fn value(&mut self, arg: u32) -> u32 {
                match self.value {
                    Some(v) => v,
                    None => {
                        let v = (self.calculation)(arg);
                        self.value = Some(v);
                        v
                    }
                }
            }
        }

        let mut cacher = Cacher::new(|x| x + 1);
        assert_eq!(cacher.value(20), 21);
        assert_eq!(cacher.value(25), 21);
        println!("{}", cacher.value(20));
    }

    example1();
    example2();
}
